﻿using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
namespace WfConnectGL
{
#if __LINUX__
  using Display = System.IntPtr;
  internal class Glx
  {
    private const string  LIBGL = "libGL";
		private const CallingConvention CALLING_CONVENTION = CallingConvention.Winapi;
		public const int GLX_RGBA = 4;	/* true if RGBA mode */
		public const int GLX_RED_SIZE = 8;	/* number of red component bits */
		public const int GLX_GREEN_SIZE = 9;	/* number of green component bits */
		public const int GLX_BLUE_SIZE = 10;	/* number of blue component bits */
    //XVisualInfo* glXChooseVisual (Display *dpy, int screen, int *attribList);
    [DllImport( LIBGL, CallingConvention = CALLING_CONVENTION, EntryPoint = "glXChooseVisual" )]
    public static extern IntPtr glXChooseVisual( IntPtr dpy, int screen, IntPtr attriblist );
		
    public static IntPtr glXChooseVisual( IntPtr dpy, int screen, int attrib )
		{
			GCHandle h0 = GCHandle.Alloc( attrib, GCHandleType.Pinned );

			try
	    {
				return glXChooseVisual( dpy, screen, h0.AddrOfPinnedObject() );
			}
			finally
			{
				h0.Free();
			}
		}
		
    public static IntPtr glXChooseVisual( IntPtr dpy, int screen, int[] attriblist )
		{
			GCHandle h0 = GCHandle.Alloc( attriblist, GCHandleType.Pinned );

			try
			{
				return glXChooseVisual( dpy, screen, h0.AddrOfPinnedObject() );
			}
			finally
			{
				h0.Free();
			}
		}
		
    //GLXContext glXCreateContext (Display *dpy, XVisualInfo *vis, GLXContext shareList, Bool direct);
		[DllImport( LIBGL, CallingConvention = CALLING_CONVENTION, EntryPoint = "glXCreateContext" )]
		public static extern IntPtr glXCreateContext( IntPtr dpy, IntPtr vis, IntPtr shareList, bool direct );
		
    //Bool glXMakeCurrent (Display *dpy, GLXDrawable drawable, GLXContext ctx);
		[DllImport( LIBGL, CallingConvention = CALLING_CONVENTION, EntryPoint = "glXMakeCurrent" )]
		public static extern bool glXMakeCurrent( IntPtr display, IntPtr drawable, IntPtr context );
		
    //void glXDestroyContext (Display *dpy, GLXContext ctx);
		[DllImport( LIBGL, CallingConvention = CALLING_CONVENTION, EntryPoint = "glXDestroyContext" )]
		public static extern void glXDestroyContext( IntPtr dpy, IntPtr context );
    
    //void glXSwapBuffers (Display *dpy, Window win);
		[DllImport( LIBGL, CallingConvention = CALLING_CONVENTION, EntryPoint = "glXSwapBuffers" )]
		public static extern void glXSwapBuffers( IntPtr dpy, IntPtr win );

  }

  internal class XWin
  {
    private const string _dll_name = "libX11";

    [DllImport(_dll_name, EntryPoint = "XOpenDisplay")]
    extern public static IntPtr OpenDisplay([MarshalAs(UnmanagedType.LPTStr)] string display_name);
        
    [DllImport(_dll_name, EntryPoint = "XCloseDisplay")]
    extern public static void CloseDisplay(Display display);
        
    [DllImport(_dll_name, EntryPoint = "XDefaultScreen")]
    extern public static int DefaultScreen(Display display);
        
    [DllImport(_dll_name, EntryPoint = "XSync")]
    public extern static IntPtr XSync(IntPtr display, bool onoff);
  }



  public class GLRC
  {
    private IntPtr hwnd;
    private IntPtr hglrc;
    private IntPtr hdpy;
    private IntPtr hvisual;
    public GLRC(IntPtr winHandle)
    {
      this.hwnd = winHandle;
      this.hdpy = XWin.OpenDisplay(null);
      if(this.hdpy == IntPtr.Zero) System.Console.WriteLine("getCurrentDisplay failed!");
      int [] attListSgl = {Glx.GLX_RGBA, Glx.GLX_RED_SIZE, 1, Glx.GLX_GREEN_SIZE, 1, Glx.GLX_BLUE_SIZE, 1, 10};
      this.hvisual = Glx.glXChooseVisual(this.hdpy, XWin.DefaultScreen(this.hdpy), attListSgl);
      if(this.hvisual == IntPtr.Zero) System.Console.WriteLine("glXChooseVisual failed!");
      this.hglrc = Glx.glXCreateContext(this.hdpy, this.hvisual, new IntPtr(0), true);
      //Glx.glXMakeCurrent(this.hdpy, this.hwnd, this.hglrc);
    }

    public bool MakeCurrent()
    {
      return Glx.glXMakeCurrent(this.hdpy, this.hwnd, this.hglrc);
    }

    public bool ReleaseCurrent()
    {
      return Glx.glXMakeCurrent(this.hdpy, this.hwnd, IntPtr.Zero);
    }

    public bool DeleteRC()
    {
      Glx.glXDestroyContext(this.hdpy, this.hglrc);
      if(this.hglrc == IntPtr.Zero)
        return true;
      else
        return false;
    }

    public void SwapBuffers()
    {
      Glx.glXSwapBuffers(this.hdpy, this.hwnd);
    }
  }
#else
#if __WINDOWS__
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct PIXELFORMATDESCRIPTOR
    {
        /// <summary>
        /// 
        /// </summary>
        public ushort nSize;
        public ushort nVersion;
        public uint dwFlags;
        public byte iPixelType;
        public byte cColorBits;
        public byte cRedBits;
        public byte cRedShift;
        public byte cGreenBits;
        public byte cGreenShift;
        public byte cBlueBits;
        public byte cBlueShift;
        public byte cAlphaBits;
        public byte cAlphaShift;
        public byte cAccumBits;
        public byte cAccumRedBits;
        public byte cAccumGreenBits;
        public byte cAccumBlueBits;
        public byte cAccumAlphaBits;
        public byte cDepthBits;
        public byte cStencilBits;
        public byte cAuxBuffers;
        public byte iLayerType;
        public byte bReserved;
        public uint dwLayerMask;
        public uint dwVisibleMask;
        public uint dwDamageMask;
    };

    public class PFD
    {
        public const uint TYPE_RGBA = 0;
        public const uint TYPE_COLORINDEX = 1;

        /* layer types */
        public const uint MAIN_PLANE = 0;
        public const uint OVERLAY_PLANE = 1;
        public const int UNDERLAY_PLANE = (-1);

        /* PIXELFORMATDESCRIPTOR flags */
        public const uint DOUBLEBUFFER = 0x00000001;
        public const uint STEREO = 0x00000002;
        public const uint DRAW_TO_WINDOW = 0x00000004;
        public const uint DRAW_TO_BITMAP = 0x00000008;
        public const uint SUPPORT_GDI = 0x00000010;
        public const uint SUPPORT_OPENGL = 0x00000020;
        public const uint GENERIC_FORMAT = 0x00000040;
        public const uint NEED_PALETTE = 0x00000080;
        public const uint NEED_SYSTEM_PALETTE = 0x00000100;
        public const uint SWAP_EXCHANGE = 0x00000200;
        public const uint SWAP_COPY = 0x00000400;
        public const uint SWAP_LAYER_BUFFERS = 0x00000800;
        public const uint GENERIC_ACCELERATED = 0x00001000;
        public const uint SUPPORT_DIRECTDRAW = 0x00002000;
        public const uint DIRECT3D_ACCELERATED = 0x00004000;
        public const uint SUPPORT_COMPOSITION = 0x00008000;

        /* PIXELFORMATDESCRIPTOR flags for use in ChoosePixelFormat only */
        public const uint DEPTH_DONTCARE = 0x20000000;
        public const uint DOUBLEBUFFER_DONTCARE = 0x40000000;
        public const uint STEREO_DONTCARE = 0x80000000;
    }

    internal class winapi
    {
        [DllImport("user32.dll", ExactSpelling = false, EntryPoint = "GetDC", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern IntPtr GetDC(IntPtr hwnd);

        [DllImport("gdi32.dll", ExactSpelling = false, EntryPoint = "ChoosePixelFormat", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern int ChoosePixelFormat(IntPtr hdc, IntPtr ppfd);

        [DllImport("gdi32.dll", ExactSpelling = false, EntryPoint = "SetPixelFormat", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern bool SetPixelFormat(IntPtr hdc, int format, IntPtr ppfd);

        [DllImport("gdi32.dll", ExactSpelling = false, EntryPoint = "SwapBuffers", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern bool SwapBuffers(IntPtr hdc);
    }

    internal class wgl
    {
        [DllImport("opengl32.dll", ExactSpelling = false, EntryPoint = "wglCreateContext", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern IntPtr CreateContext(IntPtr hdc);

        [DllImport("opengl32.dll", ExactSpelling = false, EntryPoint = "wglMakeCurrent", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern bool MakeCurrent(IntPtr hdc, IntPtr hglrc);

        [DllImport("opengl32.dll", ExactSpelling = false, EntryPoint = "wglDeleteContext", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
        public static extern bool DeleteContext(IntPtr hglrc);
    }
    public class GLRC
    {
        private IntPtr hwnd;
        private IntPtr hdc;
        private IntPtr hglrc;
        private int PixelFormat;
        public GLRC(IntPtr haddle)
        {
            PIXELFORMATDESCRIPTOR pfd;
            pfd.nSize = 40;
            pfd.nVersion = 1;
            pfd.dwFlags = PFD.DRAW_TO_WINDOW | PFD.DRAW_TO_BITMAP | PFD.SUPPORT_OPENGL | PFD.DOUBLEBUFFER;
            pfd.iPixelType = (byte)PFD.TYPE_RGBA;
            pfd.cColorBits = 32;
            pfd.cRedBits = 0;
            pfd.cRedShift = 0;
            pfd.cGreenBits = 0;
            pfd.cGreenShift = 0;
            pfd.cBlueBits = 0;
            pfd.cBlueShift = 0;
            pfd.cAlphaBits = 0;
            pfd.cAlphaShift = 0;
            pfd.cAccumBits = 0;
            pfd.cAccumRedBits = 0;
            pfd.cAccumGreenBits = 0;
            pfd.cAccumBlueBits = 0;
            pfd.cAccumAlphaBits = 0;
            pfd.cDepthBits = 16;
            pfd.cStencilBits = 0;
            pfd.cAuxBuffers = 0;
            pfd.iLayerType = (byte)PFD.MAIN_PLANE;
            pfd.bReserved = 0;
            pfd.dwLayerMask = 0;
            pfd.dwVisibleMask = 0;
            pfd.dwDamageMask = 0;
            CreateRC(haddle, pfd);
        }

        private void CreateRC(IntPtr haddle, PIXELFORMATDESCRIPTOR pfd)
        {
            this.hwnd = haddle;
            this.hdc = winapi.GetDC(haddle);
            unsafe
            {
                PIXELFORMATDESCRIPTOR* ppfd = &pfd;
                this.PixelFormat = winapi.ChoosePixelFormat(this.hdc, (IntPtr)ppfd);
                winapi.SetPixelFormat(this.hdc, this.PixelFormat, (IntPtr)ppfd);
                this.hglrc = wgl.CreateContext(this.hdc);
                wgl.MakeCurrent(this.hdc, this.hglrc);
            }
        }

        public bool MakeCurrent()
        {
            return wgl.MakeCurrent(this.hdc, this.hglrc);
        }

        public bool ReleaseCurrent()
        {
            return wgl.MakeCurrent(this.hdc, IntPtr.Zero);
        }

        public bool DeleteRC()
        {
            return wgl.DeleteContext(this.hglrc);
        }

        public void SwapBuffers()
        {
            winapi.SwapBuffers(this.hdc);
        }
    }
#endif
#endif
}
